﻿using Sustainalytics.LongRunningTaskManager.Interface;
using Sustainalytics.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.LongRunningTaskManager
{
    /// <summary>
    /// Task result factory.
    /// </summary>
    public class LongRunningTaskResultFactory : ILongRunningTaskResultFactory
    {
        // types associated to task result types
        private static ConcurrentDictionary<LongRunningTaskResultTypeEnum, Type> _taskResultTypes;


        /// <summary>
        /// Static ctor.
        /// </summary>
        static LongRunningTaskResultFactory()
        {
            _taskResultTypes = new ConcurrentDictionary<LongRunningTaskResultTypeEnum, Type>();

            LoadTypes();
        }

        /// <summary>
        /// Load types.
        /// </summary>
        private static void LoadTypes()
        {
            // get types that are task results
            IEnumerable<Type> types = AssemblyUtility.GetTypes<ILongRunningTaskResult>().Where(t => t.IsClass && t.IsAbstract == false);

            // foreach type create task result instances to discover member values
            foreach (Type type in types)
            {
                // create long running task result
                ILongRunningTaskResult taskResult = (ILongRunningTaskResult)Activator.CreateInstance(type);

                // add type to dictionary
                _taskResultTypes.TryAdd(taskResult.Type, type);
            }
        }


        /// <summary>
        /// Get all registered task result types.
        /// </summary>
        /// <returns> Registered task result types. </returns>
        public IEnumerable<Type> GetTaskResultTypes()
        {
            var types = _taskResultTypes.ToList().Select(el => el.Value).ToList();

            return types;
        }

        /// <summary>
        /// Get Type associated to a specific task result type.
        /// </summary>
        /// <param name="taskResultType"> Task result type. </param>
        /// <returns> Type associated to a specific task result type. </returns>
        public Type GetTaskResultType(LongRunningTaskResultTypeEnum taskResultType)
        {
            Type type;

            if (_taskResultTypes.TryGetValue(taskResultType, out type) == false)
            {
                throw new LongRunningTaskResultTypeNotSupportedException(taskResultType);
            }

            return type;
        }
    }
}
